﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Policy;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TipokHostManager
{
    public class GlobalHelper
    {
        public static string MonitorLocalServiceWql
        {
            get
            {
                return "AND TargetInstance.Name like 'FetionBuiltin%'";
            }
        }

        public static string GetMasterConfigName(string serviceDeployName)
        {
            return string.Format("{0}.exe.config", serviceDeployName);
        }

        public static string GetMasterName(string serviceDeployName)
        {
            return string.Format("{0}.exe", serviceDeployName);
        }

        public static string GetMasterProcessName(string serviceDeployName)
        {
            return string.Format("{0}", serviceDeployName);
        }

        public static void CopyFolder(string srcFolder, string destFolder)
        {
            CopyFolder(srcFolder, destFolder, true);
        }

        public static void CopyFolder(string srcFolder, string destFolder, bool overwrite)
        {
            if (string.IsNullOrEmpty(srcFolder))
            {
                throw new ArgumentNullException("srcFolder", "Copy Folder Failed,SrcFolder Must be Exist!");
            }
            if (string.IsNullOrEmpty(destFolder))
            {
                throw new ArgumentNullException("destFolder", "Copy Folder Failed,DestFolder Must be Exist!");
            }
            srcFolder = srcFolder.TrimEnd(new char[] { '\\' });
            string[] allDirs = Directory.GetDirectories(srcFolder, "*", SearchOption.AllDirectories);
            foreach (string dir in allDirs)
            {
                string targetDir = Path.Combine(destFolder, dir.Remove(0, srcFolder.Length + 1));
                if (!Directory.Exists(targetDir))
                {
                    Directory.CreateDirectory(targetDir);
                }
            }
            if (!Directory.Exists(destFolder))
            {
                Directory.CreateDirectory(destFolder);
            }
            string[] allFiles = Directory.GetFiles(srcFolder, "*.*", SearchOption.AllDirectories);
            bool isReadOnly = false;
            foreach (string srcFile in allFiles)
            {
                isReadOnly = false;
                string targetFile = Path.Combine(destFolder, srcFile.Substring(srcFolder.Length).TrimStart(new char[] { '\\' }));
                if (System.IO.File.Exists(targetFile))
                {
                    if (!overwrite)
                    {
                        break;
                    }
                    isReadOnly = (System.IO.File.GetAttributes(targetFile) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
                    if (isReadOnly)
                    {
                        System.IO.File.SetAttributes(targetFile, FileAttributes.Normal);
                    }
                }
                System.IO.File.Copy(srcFile, targetFile, overwrite);
                if (isReadOnly)
                {
                    System.IO.File.SetAttributes(targetFile, FileAttributes.ReadOnly);
                }
            }
        }

        public static void DeleteFileForcibly(string fileName)
        {
            int maxTryCount = 3;
            Exception lastException = null;
            for (int i = 0; (i < maxTryCount) && System.IO.File.Exists(fileName); i++)
            {
                try
                {
                    Thread.Sleep((int)(300 * i));
                    System.IO.File.Delete(fileName);
                    return;
                }
                catch (Exception ex)
                {
                    lastException = ex;
                }
            }
            if (lastException != null)
            {
                throw lastException;
            }
        }

        public static void DeleteFolder(string dirName)
        {
            if (Directory.Exists(dirName))
            {
                foreach (string fs in Directory.GetFileSystemEntries(dirName, "*.*"))
                {
                    if (System.IO.File.Exists(fs))
                    {
                        if ((System.IO.File.GetAttributes(fs) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                        {
                            System.IO.File.SetAttributes(fs, FileAttributes.Normal);
                        }
                        System.IO.File.Delete(fs);
                    }
                    else
                    {
                        DeleteFolder(fs);
                    }
                }
                if (Directory.Exists(dirName))
                {
                    Directory.Delete(dirName);
                }
            }
        }

        public static ServiceController ExistService(string serviceName)
        {
            ServiceController[] services = ServiceController.GetServices();
            foreach (ServiceController s in services)
            {
                if (s.ServiceName == serviceName)
                {
                    s.Refresh();
                    return s;
                }
            }
            return null;
        }

        public static void InstallService(string servicePath)
        {
            InstallService(servicePath, false);
        }

        public static void InstallService(string servicePath, bool createNewDomain)
        {
            if (createNewDomain)
            {
                AppDomainSetup setup = new AppDomainSetup
                {
                    ApplicationBase = Path.GetDirectoryName(servicePath)
                };
                Assembly assem = typeof(TransactedInstaller).Assembly;
                Evidence evidence = new Evidence(assem.Evidence);
                AppDomain domain = AppDomain.CreateDomain("TestDomain", evidence, setup);
                try
                {
                    using (AssemblyInstaller ai = (AssemblyInstaller)Activator.CreateInstance(domain, assem.FullName, typeof(AssemblyInstaller).FullName).Unwrap())
                    {
                        ai.Path = servicePath;
                        ai.UseNewContext = true;
                        IDictionary mySavedState = new Hashtable();
                        ai.Install(mySavedState);
                        ai.Commit(mySavedState);
                    }
                }
                finally
                {
                    AppDomain.Unload(domain);
                }
            }
            else
            {
                using (TransactedInstaller tInstaller = new TransactedInstaller())
                {
                    using (AssemblyInstaller aInstaller = new AssemblyInstaller(servicePath, new string[0]))
                    {
                        tInstaller.Installers.Add(aInstaller);
                        tInstaller.Install(new Hashtable());
                    }
                }
            }
        }

        public static void StartService(string serviceName)
        {
            ServiceController serviceControl = ExistService(serviceName);
            if (serviceControl == null)
            {
                // throw new Exception(string.Format("ComputerName {0} Has Not Exists Service {1}", SAContext.Instance.Config.ComputerName, serviceName));
            }
            if (serviceControl.Status != ServiceControllerStatus.Stopped)
            {
                throw new NotSupportedException("Unexcepted State:" + serviceControl.Status);
            }
            serviceControl.Start();
            serviceControl.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 120));
            serviceControl.Refresh();
            if (serviceControl.Status != ServiceControllerStatus.Running)
            {
                throw new Exception(string.Format("Start Service '{0}' Failed!!!", serviceName));
            }
        }

        public static void StopService(string serviceName)
        {
            ServiceController serviceControl = ExistService(serviceName);
            if (serviceControl == null)
            {
                // throw new Exception(string.Format("ComputerName {0} Has Not Exists Service {1}", ComputerName, ServiceName));
            }
            if (serviceControl.Status != ServiceControllerStatus.Running)
            {
                throw new NotSupportedException("Unexcepted State:" + serviceControl.Status);
            }
            serviceControl.Stop();
            serviceControl.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 60));
            Process[] pList = Process.GetProcessesByName(GetMasterProcessName(serviceName));
            if ((pList != null) && (pList.Length == 1))
            {
                pList[0].WaitForExit(0x1388);
            }
            serviceControl.Refresh();
            if (serviceControl.Status != ServiceControllerStatus.Stopped)
            {
                throw new Exception(string.Format("Stop Service '{0}' Failed!!!", serviceName));
            }
        }

        public static void UnInstallService(string servicePath)
        {
            UnInstallService(servicePath, false);
        }

        public static void UnInstallService(string servicePath, bool createNewDomain)
        {
            if (createNewDomain)
            {
                AppDomainSetup setup = new AppDomainSetup
                {
                    ApplicationBase = Path.GetDirectoryName(servicePath)
                };
                Assembly assem = typeof(TransactedInstaller).Assembly;
                Evidence evidence = new Evidence(assem.Evidence);
                AppDomain domain = AppDomain.CreateDomain("TestDomain", evidence, setup);
                try
                {
                    using (AssemblyInstaller ai = (AssemblyInstaller)Activator.CreateInstance(domain, assem.FullName, typeof(AssemblyInstaller).FullName).Unwrap())
                    {
                        ai.Path = servicePath;
                        ai.CommandLine = new string[] { "u" };
                        ai.UseNewContext = true;
                        ai.Uninstall(null);
                    }
                }
                finally
                {
                    AppDomain.Unload(domain);
                }
            }
            else
            {
                using (TransactedInstaller tInstaller = new TransactedInstaller())
                {
                    using (AssemblyInstaller aInstaller = new AssemblyInstaller(servicePath, new string[] { "u" }))
                    {
                        tInstaller.Installers.Add(aInstaller);
                        tInstaller.Uninstall(null);
                    }
                }
            }
        }

        public static void PingPort(string url)
        {
            Exception _pingException = null;
            AutoResetEvent waiter = new AutoResetEvent(false);
            using (Ping pingSender = new Ping())
            {
                pingSender.PingCompleted += (object sender, PingCompletedEventArgs e) =>
                {
                    if (e.Cancelled)
                    {
                        _pingException = new Exception("Ping canceled.");
                    }
                    else if (e.Error != null)
                    {
                        _pingException = e.Error;
                    }
                    else if (e.Reply.Status == IPStatus.Success)
                    {
                        _pingException = null;
                    }
                    else
                    {
                        _pingException = new Exception("Ping Failed,Replay Is Reply.Address=" + e.Reply.Address
                            + "\r\n e.Reply.RoundtripTime" + e.Reply.RoundtripTime
                            + "\r\n e.Reply.Status" + e.Reply.Status.ToString()
                            );
                    }
                    ((AutoResetEvent)e.UserState).Set();
                };
                string data = new string('a', 0x20);
                byte[] buffer = Encoding.ASCII.GetBytes(data);
                int timeout = 0x2ee0;
                PingOptions options = new PingOptions(0x40, true);
                pingSender.SendAsync(url, timeout, buffer, options, waiter);
                waiter.WaitOne();
                if (_pingException != null)
                {
                    throw _pingException;
                }
            }
        }

        public static void TelnetPort(string url)
        {
             //
        } 
    }
}
